home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1996 March: Reference Library / Dev.CD Mar 96 RL / Dev.CD Mar 96 RL.toast / Technical Documentation / develop / develop Issue 25 / develop Issue 25 code / Flicker Free / screen buffering.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-12-19  |  14.8 KB  |  499 lines  |  [TEXT/MPS ]

  1. /*---------------------------------------------------------------------------------
  2.  
  3.     FILE:
  4.         screen buffering.c
  5.     
  6.     DESCRIPTION:
  7.         this is the implementation of a set of routines to update and use an offscreen
  8.         buffer.
  9.     
  10.     COPYRIGHT:
  11.         ©1992-5 Hugo M. Ayala
  12.         ©1992-5 Apple Computer Inc.
  13.         
  14.     CHANGE LOG:
  15.     
  16.         12/01/93        Hugo        wrote entirely from scratch at home with a cold
  17.         07/13/94        Hugo         fixed a bug while sitting in a room in Yokohama
  18.                                                 Japan while having a beer.
  19.         08/04/94        Hugo        removed the dependencies from the quickdraw device loop
  20.         07/06/95        Hugo        adapted for an article to appear in the develop journal
  21.  
  22. ---------------------------------------------------------------------------------*/
  23.  
  24. #include "screen buffering.h"
  25.  
  26. #include "Memory.h"
  27.  
  28. #include "graphics routines.h"
  29. #include "graphics toolbox.h"
  30. #include "math routines.h"
  31.  
  32. #include "GXExceptions.h"
  33.  
  34. #ifdef debugging
  35.     #include "graphics debugging.h"
  36. #endif
  37.  
  38. #pragma segment screen_buffering
  39.  
  40. struct viewPortBufferRecord    {
  41.  
  42.     gxViewGroup        group;            // the offscreen's viewgroup
  43.     gxViewDevice    device;            // the offscreen viewDevice
  44.     gxViewPort        view;                // the offscreen's viewPort.  same map & clip as the screenview
  45.     
  46.     gxShape        buffer;                    // the bitmap of the off-screen viewDevice
  47.     gxBitmap    bits;                        // the data structure from which the buffer shape was created
  48.     Handle        storage;                // a handle in MF Temp mem that contains the bits of the above bitmap
  49.  
  50.     gxTransform        offxform;        // a transform that draws any shape into the off-screen
  51.     gxTransform        on_xform;        // a transform that draws any shape on screen
  52.  
  53.     gxShape            eraser;                // a shape to erase the off-screen to the background color
  54.     gxShape            marker;                // a picture used to draw any shape passed in to the off-screen
  55.     
  56.     gxShape        updatearea;            // a shape in the window's space that represents the area to update
  57.     
  58.     short                usehalftone;    // if we need to use a halftone instead of full depth
  59.  
  60.     WindowPtr        window;                // the window into which the viewport draws
  61.     gxViewPort    parent;                // the document's parent viewport
  62.     gxViewPort    screenview;        // viewPort that we've been ask to buffer
  63.     gxShape            page;                    // the shape that we're asked to draw
  64.  
  65.     gxRectangle    bounds;                // the bounds that the offscreen buffer covers
  66.     gxMapping        invmap;                // the inverse of the offscreen viewport map
  67.     gxPoint            viewdelta;        // the last delta for the offscreen viewport
  68. };
  69.  
  70. typedef struct viewPortBufferRecord viewPortBufferRecord;
  71.  
  72. /*---------------------------------------------------------------------------------
  73.     EqualMapping
  74. ---------------------------------------------------------------------------------*/
  75. static boolean EqualMapping( const gxMapping *one, const gxMapping *two )
  76. {
  77.     long *onePtr = (long *) & one->map;
  78.     long *twoPtr = (long *) & two->map;
  79.     
  80.     short        count;
  81.     
  82.     for( count = ( sizeof( gxMapping ) / sizeof( long ) - 1 ); count >= 0; count -= 1 )
  83.         {
  84.             if( *onePtr++ != *twoPtr++ )
  85.                 return( false );
  86.         }
  87.  
  88.     return( true );
  89. }
  90. /*---------------------------------------------------------------------------------
  91.     BufferDrawing
  92. ---------------------------------------------------------------------------------*/
  93. static void BufferDrawing( viewPortBufferRecord *sbPtr, const gxRectangle *boundsPtr, 
  94.     gxViewDevice target )
  95. {
  96.     gxRectangle        bounds = *boundsPtr;
  97.  
  98.     long                     depth;
  99.     gxMapping            map;
  100.     gxMapping            savemap;
  101.     gxShape                devsh;
  102.     gxBitmap            devbits;
  103.     
  104.     long    size;
  105.     OSErr    status;    
  106.     long    gxstatus;
  107.  
  108.     gxPoint        viewloc;
  109.     gxBitmap    oldbits = sbPtr->bits;
  110.     
  111.     if( sbPtr->usehalftone )
  112.         {
  113.             devsh = nil;                                                // so we don't try and dispose of it later
  114.             
  115.             depth = 1;                                                    // halftones are shallow
  116.             sbPtr->bits.space = gxIndexedSpace;        // let gx fill in the color set for us
  117.             sbPtr->bits.set = nil;
  118.             sbPtr->bits.profile = nil;
  119.         }
  120.     else
  121.         {
  122.             GXGetBitmap( devsh = GXGetViewDeviceBitmap( target ), &devbits, nil );
  123.             
  124.             depth = devbits.pixelSize;        // we use the same bitdepth of the device
  125.  
  126.             sbPtr->bits.space = devbits.space;
  127.             sbPtr->bits.set = devbits.set;
  128.             sbPtr->bits.profile = devbits.profile;
  129.         }
  130.  
  131.     // the bounds that we get to begin with are in the space of the local viewdevice
  132.     
  133.     GXGetViewDeviceMapping( target, &map );
  134.     InvertMapping( &map, &map );
  135.     MapPoints( &map, 2, (gxPoint *) &bounds );
  136.     GXGetViewPortGlobalMapping( sbPtr->parent, &map );
  137.     InvertMapping( &map, &map );
  138.     MapPoints( &map, 2, (gxPoint *) &bounds );
  139.     
  140.     // now the bounds are in global space
  141.     
  142.     sbPtr->bits.width = FixedTruncate( bounds.right + 0xFFFF ) - FixedTruncate( bounds.left );
  143.     sbPtr->bits.height = FixedTruncate( bounds.bottom + 0xFFFF ) - FixedTruncate( bounds.top );
  144.     sbPtr->bits.rowBytes = ( ( sbPtr->bits.width * depth + 31 ) >> 5 ) << 2;
  145.     sbPtr->bits.pixelSize = depth;
  146.                 
  147.     // this point is expressed in global coordinates but we want it in page coordinates
  148.  
  149.     viewloc.x = bounds.left;            // these numbers are already in window space
  150.     viewloc.y = bounds.top;
  151.     
  152.     GXGetTransformMapping( sbPtr->on_xform, &savemap );
  153.     map = savemap;
  154.     MoveMappingTo( &map, viewloc.x, viewloc.y );                        // this makes the os at 0,0 draw at x,y
  155.     
  156.     if( ! EqualMapping( &map, &savemap ) )
  157.         {
  158.             GXSetTransformMapping( sbPtr->on_xform, &map );
  159.         }
  160.  
  161.     GXGetViewPortMapping( sbPtr->view, &map );
  162.  
  163.     if( ( map.map[2][0] != sbPtr->viewdelta.x - viewloc.x ) ||
  164.             ( map.map[2][1] != sbPtr->viewdelta.y - viewloc.y ) )
  165.         {
  166.             // this makes the page at x,y draw at 0,0
  167.             
  168.             map.map[2][0] = sbPtr->viewdelta.x - viewloc.x;        // this is like a MoveMapping( -viewloc )
  169.             map.map[2][1] = sbPtr->viewdelta.y - viewloc.y;
  170.             
  171.             GXSetViewPortMapping( sbPtr->view, &map );
  172.         }
  173.  
  174.     // we have all the entries of our offscreen bitmap set up except for allocating the actual bits
  175.     
  176.     size = sbPtr->bits.rowBytes * sbPtr->bits.height;
  177.     
  178.     check( size > 0 );
  179.     if( sbPtr->storage )
  180.         {
  181.             if( (* (sbPtr->storage)) != nil )
  182.                 SetHandleSize( sbPtr->storage, size );
  183.             else
  184.                 ReallocHandle( sbPtr->storage, size );
  185.             
  186.             nrequire( status = MemError(), TempBufferFailed );
  187.         }
  188.     else
  189.         require( sbPtr->storage = TempNewHandle( size, &status ), TempBufferFailed );
  190.     
  191.     HNoPurge( sbPtr->storage );
  192.     HLock( sbPtr->storage );
  193.     
  194.     sbPtr->bits.image = * ((void **) sbPtr->storage );
  195.     
  196.     // take a look to see if we need to invalidate all of the world when we do this
  197.  
  198.     if( oldbits.image != sbPtr->bits.image        ||
  199.             oldbits.width != sbPtr->bits.width     ||
  200.             oldbits.height != sbPtr->bits.height ||
  201.             oldbits.rowBytes != sbPtr->bits.rowBytes     ||
  202.             oldbits.pixelSize != sbPtr->bits.pixelSize    ||
  203.             oldbits.space != sbPtr->bits.space                    ||
  204.             ( oldbits.set != sbPtr->bits.set && 
  205.                 oldbits.set && 
  206.                 GXEqualColorSet( oldbits.set, sbPtr->bits.set ) == false )    ||
  207.             ( oldbits.profile != sbPtr->bits.profile &&
  208.                 oldbits.profile &&
  209.                 GXEqualColorProfile( oldbits.profile, sbPtr->bits.profile    ) == false ) )
  210.         {
  211.             GXSetBitmap( sbPtr->buffer, &sbPtr->bits, nil );
  212.             GXSetViewDeviceBitmap( sbPtr->device, sbPtr->buffer );
  213.         }
  214.     else
  215.         {
  216.             sbPtr->bits.set = oldbits.set;                        // so we test this one instead of the one that got disposed
  217.             sbPtr->bits.profile = oldbits.profile;        // ditto
  218.         }
  219.     
  220.     // erase the offscreen, draw the shape into it, and then copy it on screen
  221.  
  222.     GXDrawShape( sbPtr->eraser );        // erase
  223.     GXDrawShape( sbPtr->marker );        // buffer
  224.     GXDrawShape( sbPtr->buffer );        // transfer -- done
  225.  
  226.     HUnlock( sbPtr->storage );
  227.     HPurge( sbPtr->storage );
  228.     
  229.     if( devsh ) GXDisposeShape( devsh );            // dispose the device bitmap
  230.     
  231.     GXGetGraphicsError( &gxstatus );                    ncheck( gxstatus );
  232.     if( gxstatus ) goto DrawingFailed;
  233.  
  234.     return;
  235.     
  236. TempBufferFailed:    
  237.     GXDisposeShape( devsh );            // dispose the device bitmap
  238.  
  239. DrawingFailed:
  240.     GXDrawShape( sbPtr->updatearea );
  241.     GXDrawShape( sbPtr->page );
  242. }
  243.  
  244. /*---------------------------------------------------------------------------------
  245.  
  246.     NewViewPortWBuffer
  247.  
  248. ---------------------------------------------------------------------------------*/
  249. viewPortBuffer NewViewPortWBuffer( WindowPtr window, gxViewPort view, 
  250.     const gxColor *backcolorPtr )
  251. {
  252.     Handle sbHdl;
  253.     
  254.     if( sbHdl = NewHandleClear( sizeof( viewPortBufferRecord ) ) )
  255.         {
  256.             gxInk        background;
  257.             
  258.             gxHalftone    halftone;
  259.  
  260.             viewPortBufferRecord *sbPtr;
  261.             
  262.             HLock( sbHdl ); sbPtr = * (viewPortBufferRecord **) sbHdl;
  263.     
  264.             sbPtr->window = window;
  265.             sbPtr->screenview = view;
  266.             sbPtr->parent = GXGetViewPortParent( view );
  267.                 
  268.             sbPtr->storage = nil;                                    // we don't allocate storage until we need it
  269.             sbPtr->buffer = GXNewShape( gxBitmapType );
  270.             sbPtr->group = GXNewViewGroup();
  271.             sbPtr->view = GXNewViewPort( sbPtr->group );
  272.             sbPtr->device = GXNewViewDevice( sbPtr->group, sbPtr->buffer );
  273.         
  274.             if( sbPtr->usehalftone = GXGetViewPortHalftone( view, &halftone ) )
  275.                 {
  276.                     GXSetViewPortHalftone( sbPtr->view, &halftone );
  277.                 }
  278.             
  279.             sbPtr->offxform = GXNewTransform();
  280.             GXSetTransformViewPorts( sbPtr->offxform, 1, &sbPtr->view );
  281.         
  282.             sbPtr->on_xform = GXNewTransform();
  283.             GXSetTransformViewPorts( sbPtr->on_xform, 1, &sbPtr->parent );
  284.             
  285.             background = GXNewInk();
  286.  
  287.             if( backcolorPtr )
  288.                 {
  289.                     GXSetInkColor( background, backcolorPtr );
  290.                 }
  291.             else
  292.                 {
  293.                     gxColor backcolor;
  294.  
  295.                     backcolor.space = gxRGBSpace;
  296.                     backcolor.profile = nil;
  297.                     backcolor.element.rgb.red = 
  298.                         backcolor.element.rgb.green = 
  299.                         backcolor.element.rgb.blue = 0xFFFF
  300.  
  301.                     GXSetInkColor( background, &backcolor );
  302.                 }
  303.                         
  304.             sbPtr->eraser = GXNewShape( gxFullType );    
  305.             GXSetShapeInk( sbPtr->eraser, background );
  306.             GXDisposeInk( background );
  307.             
  308.             /* the initial bounds for the offscreen are the entire window */
  309.             
  310.             sbPtr->bounds.left = ff( window->portRect.left );
  311.             sbPtr->bounds.top = ff( window->portRect.top );
  312.             sbPtr->bounds.right = ff( window->portRect.right );
  313.             sbPtr->bounds.bottom = ff( window->portRect.bottom );
  314.  
  315.             sbPtr->updatearea = GXNewRectangle( & sbPtr->bounds );
  316.             GXSetShapeViewPorts( sbPtr->updatearea, 1, & sbPtr->parent );
  317.                 
  318.             sbPtr->marker = GXNewShape( gxPictureType );
  319.             
  320.             GXSetShapeTransform( sbPtr->eraser, sbPtr->offxform );
  321.             GXSetShapeTransform( sbPtr->marker, sbPtr->offxform );
  322.             GXSetShapeTransform( sbPtr->buffer, sbPtr->on_xform );
  323.             
  324.             ResetMapping( &sbPtr->invmap );
  325.             
  326.             /* the rest of the fields in the block are initilized to zero courtersy */
  327.             /* of the "Clear" in the NewHandleClear used to allocate this block */
  328.             
  329.             HUnlock( sbHdl );
  330.         }
  331.  
  332.     return((viewPortBuffer) sbHdl);
  333. }
  334.  
  335. /*---------------------------------------------------------------------------------
  336.  
  337.     DisposeViewPortWBuffer
  338.  
  339. ---------------------------------------------------------------------------------*/
  340. void DisposeViewPortWBuffer( viewPortBuffer sb )
  341. {
  342.     viewPortBufferRecord *sbPtr;
  343.     
  344.     HLock((Handle) sb ); sbPtr = *sb;
  345.  
  346.     // we need to dispose of all of the things that we allocated
  347.  
  348.     GXDisposeShape( sbPtr->marker );
  349.     GXDisposeShape( sbPtr->eraser );
  350.  
  351.     GXDisposeTransform( sbPtr->on_xform );
  352.     GXDisposeTransform( sbPtr->offxform );
  353.     GXDisposeViewDevice( sbPtr->device );
  354.     GXDisposeViewPort( sbPtr->view );
  355.     GXDisposeViewGroup( sbPtr->group );
  356.  
  357.     GXDisposeShape( sbPtr->buffer );
  358.  
  359.     if( sbPtr->storage ) DisposeHandle( sbPtr->storage );
  360.     
  361.     HUnlock((Handle) sb );
  362.     DisposeHandle((Handle) sb );
  363. }
  364.  
  365. /*---------------------------------------------------------------------------------
  366.  
  367.     UpdateViewPortWBuffer
  368.     
  369. ---------------------------------------------------------------------------------*/
  370. void UpdateViewPortWBuffer( viewPortBuffer sb, gxShape clip, gxMapping *viewmap )
  371. {
  372.     viewPortBufferRecord    *sbPtr;
  373.     gxHalftone halftone;
  374.     
  375.     HLock((Handle) sb ); sbPtr = *sb;
  376.     
  377.     if( viewmap )
  378.         {
  379.             GXSetViewPortMapping( sbPtr->view, viewmap );
  380.             InvertMapping( &sbPtr->invmap, viewmap );
  381.  
  382.             sbPtr->viewdelta.x = viewmap->map[2][0];
  383.             sbPtr->viewdelta.y = viewmap->map[2][1];
  384.         }
  385.     
  386.     if( clip )
  387.         {
  388.             // the clip is in display space (local space for the window)
  389.             
  390.             GXSetShapeClip( sbPtr->updatearea, clip );
  391.             GXGetShapeBounds( clip, 0, & sbPtr->bounds );
  392.         }
  393.     else
  394.         {
  395.             WindowPtr    window = sbPtr->window;
  396.             
  397.             sbPtr->bounds.left = ff( window->portRect.left );
  398.             sbPtr->bounds.top = ff( window->portRect.top );
  399.             sbPtr->bounds.right = ff( window->portRect.right );
  400.             sbPtr->bounds.bottom = ff( window->portRect.bottom );
  401.         }
  402.         
  403.     if( sbPtr->usehalftone = GXGetViewPortHalftone( sbPtr->screenview, &halftone ) )
  404.         GXSetViewPortHalftone( sbPtr->view, &halftone );
  405.     else
  406.         GXSetViewPortHalftone( sbPtr->view, nil );            
  407.  
  408.     HUnlock((Handle) sb );
  409. }
  410.  
  411. /*---------------------------------------------------------------------------------
  412.  
  413.     SetViewPortWBufferDither
  414.  
  415. ---------------------------------------------------------------------------------*/
  416. void SetViewPortWBufferDither( viewPortBuffer sb, const long dither )
  417. {
  418.     GXSetViewPortDither( (*sb)->view, dither );
  419. }
  420. /*---------------------------------------------------------------------------------
  421.  
  422.     DrawShapeBuffered
  423.         
  424.         This is the routine that implements the drawing of shapes through a 
  425.         doubled buffered viewport.
  426.         
  427.         If a rectangle is specified then it is treated as being inside of the space
  428.         of the viewport which defines this record (sbPtr->view).  as it turns out this
  429.         is more work for this routine, since it has to compute the bounds as expressed
  430.         in window space in order to figure out what it needs to draw.  However, since
  431.         most drawing and updating requests will come in the space of the window already,
  432.         it seem correct to leave it to this function to figure out where drawing should
  433.         happen.
  434.     
  435. ---------------------------------------------------------------------------------*/
  436. void DrawShapeBuffered( viewPortBuffer sb, gxShape page, const gxRectangle *updatebounds )
  437. {
  438.     viewPortBufferRecord *sbPtr;
  439.     gxRectangle bounds;
  440.     
  441.     HLock((Handle) sb); sbPtr = *sb;
  442.         
  443.     if( updatebounds )
  444.         {
  445.             gxMapping        map;
  446.             
  447.             GXGetViewPortMapping( sbPtr->screenview, &map );
  448.             bounds = *updatebounds;
  449.             
  450.             bounds.left = bounds.left & 0xFFFF0000;
  451.             bounds.right = ( bounds.right + 0xFFFF ) & 0xFFFF0000;
  452.  
  453.             bounds.top = bounds.top & 0xFFFF0000;
  454.             bounds.bottom = ( bounds.bottom + 0xFFFF ) & 0xFFFF0000;
  455.             
  456.             MapPoints( &map, 2, (gxPoint *) &bounds );
  457.  
  458.             bounds.left = bounds.left & 0xFFFF0000;
  459.             bounds.right = ( bounds.right + 0xFFFF ) & 0xFFFF0000;
  460.  
  461.             bounds.top = bounds.top & 0xFFFF0000;
  462.             bounds.bottom = ( bounds.bottom + 0xFFFF ) & 0xFFFF0000;
  463.         }
  464.     else
  465.         bounds = sbPtr->bounds;
  466.         
  467.     // the above given bounds are in the window space -- just right
  468.     
  469.     GXSetRectangle( sbPtr->updatearea, &bounds );
  470.  
  471.     // check to see that the shape is actually visible on the screen and the proceed to draw
  472.     
  473.     if( bounds.left < bounds.right && bounds.top < bounds.bottom )
  474.         {
  475.             GDHandle        screen;
  476.             
  477.             if( sbPtr->page != page )
  478.                 {
  479.                     GXSetPicture( sbPtr->marker, 1, & page, nil, nil, nil );
  480.                     sbPtr->page = page;
  481.                 }
  482.  
  483.             if( screen = GetDeviceList() )
  484.                 {
  485.                     do
  486.                         {
  487.                             gxViewDevice device = GXGetGDeviceViewDevice( screen );
  488.                             
  489.                             // note that we re-use the bounds in here
  490.                             
  491.                             if( GXGetShapeDeviceBounds( sbPtr->updatearea, sbPtr->parent, device, &bounds ) )
  492.                                 {
  493.                                     BufferDrawing( sbPtr, &bounds, device );
  494.                                 }
  495.                     
  496.                         } while( screen = GetNextDevice( screen )  );
  497.                 }
  498.         }
  499. }